package cn.org.xiaoweiba.graduationdesign.bookmall.aspect;

import cn.org.xiaoweiba.graduationdesign.bookmall.annotation.rsa.DecodeRsaCommonAnnotation;
import cn.org.xiaoweiba.graduationdesign.bookmall.annotation.rsa.DecodeRsaCommonObjectAnnotation;
import cn.org.xiaoweiba.graduationdesign.bookmall.annotation.rsa.DecodeRsaCommonParameterAnnotation;
import cn.org.xiaoweiba.graduationdesign.bookmall.constant.ResponseResultMessageConstant;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.RSAKeyPair;
import cn.org.xiaoweiba.graduationdesign.bookmall.dto.result.ServerErrorResponseResult;
import cn.org.xiaoweiba.graduationdesign.bookmall.utils.AspectUtil;
import cn.org.xiaoweiba.graduationdesign.bookmall.utils.RSAUtilInteractiveWithFrontEnd;
import cn.org.xiaoweiba.graduationdesign.bookmall.utils.ReflectUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

/**
 * 和 RSA 加密解密处理相关的切面类
 */
@Aspect
@Order(2)
@Component
public class RsaAspect {

    /**
     * RSA 通用密钥对
     */
    @Autowired
    private RSAKeyPair rsaCommonKeyPair;

    /**
     * RsaAspect 的切点为被 @DecodeRsaCommonAnnotation 标记的位置
     */
    @Pointcut("@annotation(cn.org.xiaoweiba.graduationdesign.bookmall.annotation.rsa.DecodeRsaCommonAnnotation)")
    public void pointCut() {
    }

    /**
     * 采用 Rsa 加密算法进行解密
     *
     * @param proceedingJoinPoint 切点
     */
    @Around("pointCut()")
    public Object decodeRsaAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        try {
            // 执行方法参数处理的环绕通知增强代码，对被注解 DecodeRsaCommonObjectAnnotation 或
            // DecodeRsaCommonParameterAnnotation 标注的方法参数进行相应的 RSA 解密处理
            return AspectUtil.doParamHandleAroundAdvice(
                    proceedingJoinPoint,
                    DecodeRsaCommonObjectAnnotation.class,
                    (Object object) -> {
                        try {
                            // 对切点方法的对象形式的参数中被 DecodeRsaCommonAnnotation 标注的字段进行 RSA 解密
                            decodeRsaObjectField(object);
                            // doParamHandleAroundAdvice 方法中，调用此方法的位置无需返回值，所以返回 null
                            return null;
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    },
                    DecodeRsaCommonParameterAnnotation.class,
                    (Object param) -> {
                        try {
                            // 对被 DecodeRsaParameterAnnotation 标注的参数进行 RSA 加密算法解密。
                            return decodeRsaParameter((String) param);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
            );
        } catch (Throwable e) {
            // 响应服务器异常
            return ServerErrorResponseResult.errorResult(ResponseResultMessageConstant.SERVER_ERROR);
        }
    }

    /**
     * 对指定对象中被 DecodeRsaCommonAnnotation 标注的字段进行 RSA 加密算法解密
     *
     * @param object 需要进行解密的对象
     */
    private void decodeRsaObjectField(Object object) throws Exception {
        try {
            ReflectUtil.doObjectAllFieldHandleByAnnotation(
                    object,
                    (Object obj, Field field, Annotation[] annotations) -> {
                        try {
                            // 当前字段没有被 DecodeRsaCommonAnnotation 标注直接判断下一个字段
                            if (!ReflectUtil.judgeContainAnnotation(annotations, DecodeRsaCommonAnnotation.class)) {
                                return;
                            }
                            // 当前字段被 DecodeRsaCommonAnnotation 标注，需要进行解密处理
                            // 获取当前字段在指定对象中的值
                            Object fieldValue = ReflectUtil.getFieldValue(obj, field);
                            // 获取当前字段在指定类中的 setter 方法
                            Method fieldSetMethod = ReflectUtil.getFieldSetMethod(field, obj.getClass());
                            // 对当前字段进行解密处理，然后调用 setter 方法为字段重新赋值
                            fieldSetMethod.invoke(
                                    object,
                                    RSAUtilInteractiveWithFrontEnd.decryptByPrivateKey(
                                            rsaCommonKeyPair.getPrivateKey(),
                                            (String) fieldValue
                                    )
                            );
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 对被 DecodeRsaParameterAnnotation 标注的参数进行 RSA 加密算法解密。
     * 由于数据加密后一定为字符串类型，所以此方法约定接收的参数的类型为字符串类型。
     *
     * @param param 需要进行解密的参数
     */
    private String decodeRsaParameter(String param)
            throws NoSuchPaddingException, IllegalBlockSizeException,
            NoSuchAlgorithmException, InvalidKeySpecException, IOException,
            BadPaddingException, InvalidKeyException {
        // 进行 RSA 加密算法解密
        return RSAUtilInteractiveWithFrontEnd.decryptByPrivateKey(rsaCommonKeyPair.getPrivateKey(), param);
    }


}
